package com.yntx.usercenter.common.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;

/**
 * Description: Json工具类
 *
 * @author Liziba
 * @version 1.0
 * @date 2022/10/13 11:43 AM
 * @since JDK 1.8
 */
public class JsonUtil {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
    private static final String SIMPLE_MODULE_BOOLEAN_NAME = "BooleanConvertModule";
    private static final String SIMPLE_MODULE_BIG_DECIMAL_NAME = "BigDecimalConvertModule";

    static {
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        OBJECT_MAPPER.setDateFormat(new SimpleDateFormat(DATE_FORMAT_PATTERN));
        OBJECT_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);
        SimpleModule booleanModule = new SimpleModule(SIMPLE_MODULE_BOOLEAN_NAME);
        booleanModule.addDeserializer(Boolean.class, new NumberDeserializers.BooleanDeserializer(Boolean.class, Boolean.FALSE));
        OBJECT_MAPPER.registerModule(booleanModule);
        SimpleModule decimalModule = new SimpleModule(SIMPLE_MODULE_BIG_DECIMAL_NAME);
        decimalModule.addSerializer(BigDecimal.class, new ToStringSerializer());
        OBJECT_MAPPER.registerModule(decimalModule);
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        OBJECT_MAPPER.configure(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, true);
        OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
        OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    /**
     * object转json
     *
     * @param object
     * @return
     */
    public static String toJson(Object object) {
        if (object == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * object转json && 写入流
     *
     * @param object
     * @param out
     */
    public static void toJson(Object object, OutputStream out) {
        if (object == null) {
            return;
        }
        try {
            OBJECT_MAPPER.writeValue(out, object);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json转object
     *
     * @param json
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T toBean(String json, Class<T> clazz) {
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 流中读取json转object
     *
     * @param in
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T toBean(InputStream in, Class<T> clazz) {
        if (in == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(in, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json转object
     *
     * @param json
     * @param reference
     * @param <T>
     * @return
     */
    public static <T> T toBean(String json, TypeReference<T> reference) {
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, reference);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 流中读取json转object
     *
     * @param in
     * @param reference
     * @param <T>
     * @return
     */
    public static <T> T toBean(InputStream in, TypeReference<T> reference) {
        if (in == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(in, reference);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json转object
     *
     * @param json
     * @param javaType
     * @param <T>
     * @return
     */
    public static <T> T toBean(String json, JavaType javaType) {
        if (StringUtils.isBlank(json) || javaType == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, javaType);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 范型集合获取JavaType
     *
     * @param parametrized
     * @param parameterClasses
     * @return
     */
    public static JavaType getCollectionType(Class<?> parametrized, Class<?>... parameterClasses) {
        return OBJECT_MAPPER.getTypeFactory().constructParametricType(parametrized, parameterClasses);
    }

    /**
     * json串转Json对象
     *
     * @param json
     * @return
     */
    public static JsonNode parseObject(String json) {
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readTree(json);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json格式化
     *
     * @param json
     * @return
     */
    public static String jsonFormat(String json) {
        if (StringUtils.isBlank(json)) {
            return null;
        }

        int level = 0;
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < json.length(); i++) {
            char c = json.charAt(i);
            if (level > 0 && '\n' == sb.charAt(sb.length() - 1)) {
                sb.append(getLevelStr(level));
            }
            switch (c) {
                case '{':
                case '[':
                    sb.append(c).append("\n");
                    level++;
                    break;
                case ',':
                    sb.append(c).append("\n");
                    break;
                case '}':
                case ']':
                    sb.append("\n");
                    level--;
                    sb.append(getLevelStr(level));
                    sb.append(c);
                    break;
                default:
                    sb.append(c);
                    break;
            }
        }

        return sb.toString();
    }

    private static String getLevelStr(int level) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < level; i++) {
            sb.append("\t");
        }
        return sb.toString();
    }

    public static <T> String parseInPrettyMode(T object) {
        try {
            return OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throwIfUnchecked(e);
            throw new IllegalArgumentException(e);
        }
    }

    public static void throwIfUnchecked(Throwable throwable) {
        Assert.notNull(throwable, "exception is null");
        if (throwable instanceof RuntimeException) {
            throw (RuntimeException) throwable;
        } else if (throwable instanceof Error) {
            throw (Error) throwable;
        }
    }

}
